package com.jty.lib_base.tsdBlePen

import android.app.NotificationChannel
import android.app.NotificationManager
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.ContentObserver
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.location.LocationManager
import android.os.Binder
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.provider.Settings
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.view.animation.LinearInterpolator
import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.app.NotificationCompat
import androidx.lifecycle.LifecycleService
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.GsonUtils
import com.blankj.utilcode.util.ScreenUtils
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.czl.lib_base.config.AppConstants
import com.jty.lib_base.R
import com.jty.lib_base.base.AppManager
import com.jty.lib_base.base.BaseBean
import com.jty.lib_base.data.DataRepository
import com.jty.lib_base.data.bean.*
import com.jty.lib_base.event.LiveBusCenter
import com.jty.lib_base.extension.ApiSubscriptHelper
import com.jty.lib_base.mvvm.ui.ContainerFmActivity
import com.jty.lib_base.oss.UploadOss
import com.jty.lib_base.room.AppDatabase
import com.jty.lib_base.room.bean.GridNoSubmit
import com.jty.lib_base.room.bean.GridPicture
import com.jty.lib_base.utils.*
import com.kongzue.dialogx.dialogs.CustomDialog
import com.kongzue.dialogx.dialogs.WaitDialog
import com.kongzue.dialogx.interfaces.DialogLifecycleCallback
import com.kongzue.dialogx.interfaces.OnBindView
import com.orhanobut.logger.Logger
import com.tstudy.blepenlib.BlePenManager
import com.tstudy.blepenlib.BlePenStreamManager
import com.tstudy.blepenlib.callback.BleGattCallback
import com.tstudy.blepenlib.callback.BlePenStreamCallback
import com.tstudy.blepenlib.callback.BleScanCallback
import com.tstudy.blepenlib.constant.Constant
import com.tstudy.blepenlib.data.BleDevice
import com.tstudy.blepenlib.data.CoordinateInfo
import com.tstudy.blepenlib.exception.BleException
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.RequestBody
import org.koin.android.ext.android.inject
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.TimeUnit


class TsdService : LifecycleService() {

    companion object {
        val ERR_TSD_PEN_INIT = 101 //蓝牙笔初始化错误
        val ERR_TSD_GPS = 102 //位置服务没有打开
        val ERR_TSD_BT_NO_SUPPORT = 103 //不支持蓝牙ble
        val ERR_TSD_BT_NO_OPEN = 104 //蓝牙没有打开
        val ERR_TSD_BT_OPEN_T = 105 //蓝牙打开成功
        val ERR_TSD_BT_OPEN_F = 106 //蓝牙打开失败

        val CONN_PEN_START = 201 //蓝牙连接开始；
        val CONN_PEN_FAIL = 202 //蓝牙连接失败；
        val CONN_PEN_SUCCESS = 203 //蓝牙连接成功；
        val CONN_PEN_DIS = 204 //蓝牙取消连接；

        val PEN_COODINAT_MESSAGE = -114 //笔移动状态
        val PEN_DOWN_MESSAGE = -26 //笔按下状态
        val PEN_UP_MESSAGE = -25 //笔弹起状态
        val PEN_OTHER_MESSAGE = -24 //笔其他状态

        val MODULE_DEFAULT = 300 //默认无模式
        val MODULE_PEN_OFFLINE = 301 //笔的离线数据上传模式，在设置蓝牙连接界面，已连接就切到这个模式，然后判断是否有离线数据，再重新设置模式
        val MODULE_PEN_TEACHING = 302 //教研笔记界面，在线模式
        val MODULE_PEN_TEACHING_DIS = 303 //教研笔记界面，离线模式
        val MODULE_PEN_PREVIEW = 304 //我的笔迹预览界面，蓝牙笔链接，在主界面等点击时，切换到这个界面
        val MODULE_PEN_OUTLOGIN_DIALOG = 305 //退出登录弹框模式

        val ING_NO = 400
        val ING_HTTP = 401 //正在请求后台数据模式
        val ING_ACTIVITY_SWITCH = 402 //正在切换界面模式

        val IMG_SCALE = 1f;


    }




    var mBleDevice: BleDevice? = null //通过这个参数来判断是否连接
    private var mReceive //监听蓝牙状态
            : BluetoothStateBroadcastReceive? = null
    private var mLocationManager //监听gps状态
            : LocationManager? = null
    private val mBinder: LocalBinder = LocalBinder()
    private var mTsdServiceTag: String? = null
    private var mIsTsbInitOk = false //笔和蓝牙是否初始化成功
    private var mIsScaning = false //正在搜索笔
    private var mScanCallback: BleScanCallback? = null
    private var mBleGattCallback: BleGattCallback? = null
    private var mBlePenStreamCallback: BlePenStreamCallback? = null
    private var mFailReConnectCount = 0 //有时连接失败了，但是灯却是蓝色的。
    private var mCoordinateInfoTemp: CoordinateInfo? = null
    private val mSimpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS", Locale.CHINA)
    @Volatile private var mModule = MODULE_DEFAULT
    @Volatile private var mIng = ING_NO
    private val dataRepository: DataRepository by inject()
    private var mPenMac = ""
    private lateinit var mScanDialog:CustomDialog
    private var scanAnim: Animation? = null
    private var iv_scan: ImageView? = null
    private lateinit var mProgressDialog:CustomDialog
    private var progress_tv_bar: TextView? = null
    private var progress_pb_bar: ProgressBar? = null
    private val mPenListAdapter = PenListAdapter()
    private var mBatteryParent = 0
    private var mMemoryPercent = 0
    private var mMemoryByteNum = 0
    private val mQDots: ConcurrentLinkedQueue<CoordinateInfo> = ConcurrentLinkedQueue<CoordinateInfo>()
    private var mCurDot: CoordinateInfo? = null
    private var mOffLineDot_temp: CoordinateInfo? = null
    private var mGridPictureList_noSubmit  = listOf<GridNoSubmit>()
    private val mCommitOfflineReqS_noSubmit = mutableListOf<CommitOfflineReq>()
    private var mUploadCount_noSubmit = -1
    private var mIsGetPenInfo = false
    private var mIsFromTeach = false //离线从教研切过来的
    private lateinit var mAppVersionDialog:CustomDialog
    private var mStrVersionDesc:String = ""
    private lateinit var mVersionInfo: VersionInfoBean
    private var mDisDotOnOff = 0 //0,正常处理，1.正在处理离线数据，2.处理完离线数据，但是处理过程中有在线数据，忽略在线数据
    private var mIsMoreToast = false  //防止多次弹框


    inner class LocalBinder : Binder() {
        fun getService(): TsdService? {
            return this@TsdService
        }

        fun setTsdServiceTag(className: String) {
            mTsdServiceTag = className
            //Logger.i("m1: mTsdServiceTag = $mTsdServiceTag")
        }

        fun setTsdServiceConnected() {
            //mCurDot = null //打开这一句，第一笔会丢失
            if (!mIsTsbInitOk) {
                //initTsdPen()
            }

            if(mIsFromTeach) {
                mIng = ING_NO
                switchOffLine()
            }
        }
        fun setTsdServiceDisConnect() {
            //mCurDot = null //打开这一句，第一笔会丢失
            on_setmModule(MODULE_DEFAULT)
        }

        fun on_rePenInit() {
            //比如打开蓝牙后，返回界面重新初始化
            initTsdPen()
        }

        fun on_isScaning(): Boolean {
            return mIsScaning
        }

        fun on_startScan() {
            if (mIsTsbInitOk) {
                if (!mIsScaning) {
                    BlePenStreamManager.getInstance().scan(mScanCallback)
                }
            } else {
                Logger.e("蓝牙笔 mIsTsbInitOk = false, 禁止调用搜索,并重新初始化")
                on_rePenInit()
            }
        }

        fun on_stopScan() {
            if (mIsTsbInitOk) {
                if (mIsScaning) {
                    BlePenStreamManager.getInstance().cancelScan()
                }
            } else {
                Logger.e("蓝牙笔 mIsTsbInitOk = false, 禁止调用搜索,并重新初始化")
                on_rePenInit()
            }
        }

        fun on_finishPre() {
            BlePenStreamManager.getInstance().cancelScan()
        }

        fun on_penConnect(bleDevice: BleDevice?) {
            if (mIsScaning) {
                BlePenStreamManager.getInstance().cancelScan()
            }
            Observable.timer(2000, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Observer<Long> {
                    override fun onSubscribe(d: Disposable) {}
                    override fun onNext(aLong: Long) {
                        //Logger.d("m1:onNext $aLong, threadName = ${Thread.currentThread().name}")
                        mFailReConnectCount = 0
                        mScanDialog.dismiss()
                        BlePenStreamManager.getInstance().connect(bleDevice, mBleGattCallback, mBlePenStreamCallback)
                        //BlePenStreamManager.getInstance().connect(bleDevice, mBleGattCallback)
                    }

                    override fun onError(e: Throwable) {}
                    override fun onComplete() {}
                })
        }

        fun on_getmBleDevice(): BleDevice? {
            return mBleDevice
        }

        fun on_getPenIsConnect():Boolean{
            if(mBleDevice != null && BlePenStreamManager.getInstance().isConnected(mBleDevice)) {
                return true
            } else {
                return false
            }
        }

        fun on_setmModule(module: Int) {
            if(mIsFromTeach) {
                return
            }
            setmModule(module)
            if(module == MODULE_PEN_PREVIEW) {
                //切换界面成功后，重新分析一下当前点
                mIng = ING_NO
                if (mCurDot != null && mCurDot!!.pageAddress != null && mCurDot!!.state == PEN_DOWN_MESSAGE) {
                    //只处理按下的点，否则会导致离线获取上次处理的点也会到这个界面来
                    analysisCurDot()
                }
            }
        }

        fun on_getmModule(): Int {
            return mModule
        }

        fun on_setmIsFromTeach(isFromTeach: Boolean) {
            mIsFromTeach = isFromTeach
        }

        fun on_getmIsFromTeach(): Boolean {
            return mIsFromTeach
        }

        fun on_setmDisDotOnOff(type: Int) {
            mDisDotOnOff = type
        }

        fun on_clickConnect() {
            if (!mIsTsbInitOk) {
                initTsdPen()
            } else {
                showDialogScan()
            }
        }

    }

    override fun onBind(intent: Intent): IBinder? {
        super.onBind(intent)
        return mBinder
    }

    override fun onCreate() {
        super.onCreate()

        registerBluetoothReceiver()
        mLocationManager = getSystemService(LOCATION_SERVICE) as LocationManager
        contentResolver
            .registerContentObserver(
                Settings.Secure
                    .getUriFor(Settings.System.LOCATION_PROVIDERS_ALLOWED),
                false, mGpsMonitor
            )



        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val CHANNEL_ID_STRING = "service_01"
            val notificationManager =
                this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            var mChannel: NotificationChannel? = null
            mChannel = NotificationChannel(
                CHANNEL_ID_STRING, getString(R.string.app_name),
                NotificationManager.IMPORTANCE_LOW
            )
            notificationManager.createNotificationChannel(mChannel)
            val notification = NotificationCompat.Builder(this, CHANNEL_ID_STRING)
                .setSmallIcon(R.drawable.ic_logo)
                .setContentText("智能笔服务已开启")
                .build()

            startForeground(1, notification)
        }

        scanAnim = AnimationUtils.loadAnimation(this, R.anim.rotate)
        scanAnim?.interpolator = LinearInterpolator()

        initDialogRecyclerView()
        initDialogScan()
        initDialogProgress()
        initDialogAppVersion()

        initViewObservable()
    }

    override fun onDestroy() {
        super.onDestroy()
        if (scanAnim != null) {
            scanAnim?.cancel()
            scanAnim = null
        }

        if (mIsTsbInitOk && mBleDevice != null && BlePenStreamManager.getInstance()
                .isConnected(mBleDevice)
        ) {
            BlePenStreamManager.getInstance().disconnect(mBleDevice)
        }
        unregisterBluetoothReceiver()
        contentResolver.unregisterContentObserver(mGpsMonitor)
    }

    private fun registerBluetoothReceiver() {
        if (mReceive == null) {
            mReceive = BluetoothStateBroadcastReceive()
        }
        val intentFilter = IntentFilter()
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
        intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
        intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
        intentFilter.addAction("android.bluetooth.BluetoothAdapter.STATE_OFF")
        intentFilter.addAction("android.bluetooth.BluetoothAdapter.STATE_ON")
        registerReceiver(mReceive, intentFilter)
    }

    private fun unregisterBluetoothReceiver() {
        if (mReceive != null) {
            unregisterReceiver(mReceive)
            mReceive = null
        }
    }

    inner class BluetoothStateBroadcastReceive : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val action = intent.action
            val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
            when (action) {
                BluetoothDevice.ACTION_ACL_CONNECTED -> {
                }
                BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
                }
                BluetoothAdapter.ACTION_STATE_CHANGED -> {
                    refreshGpsIsOpen()
                    ThreadUtils.doOnUIThread(object : ThreadUtils.UITask {
                        override fun doOnUI() {
                            Logger.e("蓝牙状态改变了。。。。")
                            refreshGpsIsOpen()
                            val blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0)
                            when (blueState) {
                                BluetoothAdapter.STATE_OFF -> ToastHelper.showNormalToast("蓝牙已关闭")
                                BluetoothAdapter.STATE_ON -> ToastHelper.showNormalToast("蓝牙已开启")
                            }
                        }

                    })
                }
            }
        }
    }

    private val mGpsMonitor: ContentObserver = object : ContentObserver(null) {
        override fun onChange(selfChange: Boolean) {
            super.onChange(selfChange)
            ThreadUtils.doOnUIThread(object : ThreadUtils.UITask {
                override fun doOnUI() {
                    val enabled = mLocationManager?.isProviderEnabled(LocationManager.GPS_PROVIDER)
                    Logger.e("GPS状态改变了。。。")
                    refreshGpsIsOpen()
                    enabled?.let {
                        if (enabled) {
                            ToastHelper.showNormalToast("gps已开启")
                        } else {
                            ToastHelper.showNormalToast("gps已关闭")
                        }
                    }
                }

            })
        }
    }

    private fun initTsdPen() {
        val initSuccess = BlePenStreamManager.getInstance().init(applicationContext, MyLicense.getBytes())
        if (initSuccess) {
            BlePenStreamManager.getInstance().enableLog(true)
            initTsdPenListener()
            refreshGpsIsOpen()
        } else {
            ToastHelper.showNormalToast( "初始化失败，请到开放平台申请授权或检查设备是否支持蓝牙BLE")
            onDataReceiveListener?.onErrTsdPen(TsdService.ERR_TSD_PEN_INIT, "初始化失败，请到开放平台申请授权或检查设备是否支持蓝牙BLE")
        }

        if(mIsTsbInitOk) showDialogScan()
    }

    private fun refreshGpsIsOpen() {
        if (onDataReceiveListener == null) {
            return
        }
        if (!gpsIsOpen() && onDataReceiveListener != null) {
            onDataReceiveListener?.onErrTsdPen(TsdService.ERR_TSD_GPS, "位置服务关闭，请点击打开")
            disconnectBlePen()
            mIsTsbInitOk = false

            dialogCommon(2, "位置服务关闭，请点击打开")
        } else {
            checkBleOpen()
        }
    }

    private fun checkBleOpen() {
        val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        if (bluetoothAdapter == null) {
            onDataReceiveListener?.onErrTsdPen(TsdService.ERR_TSD_BT_NO_SUPPORT, "检查设备是否支持蓝牙BLE")
            disconnectBlePen()
            mIsTsbInitOk = false

            dialogCommon(1, "检查设备是否支持蓝牙BLE")
            return
        }
        bluetoothAdapter?.let {
            if (!bluetoothAdapter.isEnabled) {
                onDataReceiveListener?.onErrTsdPen(TsdService.ERR_TSD_BT_NO_OPEN, "请先打开蓝牙")
                disconnectBlePen()
                mIsTsbInitOk = false

                dialogCommon(1, "请先打开蓝牙")
                return@checkBleOpen
            }
        }

        onDataReceiveListener?.onErrTsdPen(TsdService.ERR_TSD_BT_OPEN_T, "蓝牙打开成功")
        mIsTsbInitOk = true
    }

    /**
     * 判断GPS是否开启，GPS或者AGPS开启一个就认为是开启的
     *
     * @return true 表示开启
     */
    fun gpsIsOpen(): Boolean {
        val locationManager =
            applicationContext.getSystemService(LOCATION_SERVICE) as LocationManager
        // 通过GPS卫星定位，定位级别可以精确到街（通过24颗卫星定位，在室外和空旷的地方定位准确、速度快）
        val gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
        val passive = locationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER)
        // 通过WLAN或移动网络(3G/2G)确定的位置（也称作AGPS，辅助GPS定位。主要用于在室内或遮盖物（建筑群或茂密的深林等）密集的地方定位）
        val network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
        Logger.i("gpsIsOpen: gps$gps    passive$passive     network $network")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //版本大于6.0，必须打开gps才可以搜索到蓝牙
            if (gps) {
                return true
            }
        } else {
            //在版本低于此的时候，做一些处理
            if (gps || network || passive) {
                return true
            }
        }
        return false
    }

    private fun disconnectBlePen() {
        if (mBleDevice != null && mIsTsbInitOk && BlePenStreamManager.getInstance() != null && BlePenStreamManager.getInstance()
                .isConnected(mBleDevice)
        ) {
            BlePenStreamManager.getInstance().disconnect(mBleDevice)
        }
    }

    private fun initTsdPenListener() {
        //通讯数据流回调
        mBlePenStreamCallback = object : BlePenStreamCallback {

            override fun onRemainBatteryAndMemory(
                batteryPercent: Int,
                memoryPercent: Int,
                memoryByteNum: Int
            ) {
                Logger.i("batteryPercent: $batteryPercent%, memoryPercent: $memoryPercent%, memoryByteNum:$memoryByteNum, threadName = ${Thread.currentThread().name}")
                mBatteryParent = batteryPercent
                mMemoryPercent = memoryPercent
                mMemoryByteNum = batteryPercent
                onDataReceiveListener?.onGetPenInfo(batteryPercent, memoryPercent, memoryByteNum)
                if(mModule != MODULE_PEN_OFFLINE && memoryByteNum > 0 && mIsGetPenInfo) {
                    mDisDotOnOff = 1
                    if(mModule == MODULE_PEN_TEACHING || mModule == MODULE_PEN_TEACHING_DIS) {
                        mIsFromTeach = true
                        mIng = ING_ACTIVITY_SWITCH
                        /*startContainerActivity(AppConstants.Router.Main.F_HOME, Bundle().apply {
                            putBoolean("mIsFromTeach", mIsFromTeach)
                        })*/
                        startContainerActivity(AppConstants.Router.Main.F_HOME)
                    } else {
                        switchOffLine()
                    }
                }
                if(!mIsFromTeach) {
                    mIsGetPenInfo = false
                }
            }

            override fun onCoordDraw(coordinateInfo: CoordinateInfo) {
                /**
                 * 输出离线数据坐标信息
                 * @param state down/move/up
                 * @param pageAddress 点阵地址
                 * @param nX 坐标X
                 * @param nY 坐标Y
                 * @param nForce 压力值
                 * @param strokeNum 笔画数
                 * @param time 时间戳
                 * @param isOFFLine  是否是离线数据
                 * @param offLineDataAllSize 总离线字节数
                 * @param offLineDateCurrentSize 已上传字节数
                 */
                /*Logger.i(
                    ", state: " + coordinateInfo.state
                            + ", strokeNum: " + coordinateInfo.strokeNum
                            + ", pageAddress: " + coordinateInfo.pageAddress
                            + ", coordForce: " + coordinateInfo.coordForce
                            + ", x: " + coordinateInfo.coordX
                            + ", y: " + coordinateInfo.coordY
                            + ", isOFFLine: " + coordinateInfo.isOFFLine
                            + ", offLineDataAllSize: " + coordinateInfo.offLineDataAllSize
                            + ", offLineDateCurrentSize: " + coordinateInfo.offLineDateCurrentSize
                            + ", timeLong: " + coordinateInfo.timeLong
                            + ", threadName = " + Thread.currentThread().name
                            + ",img =" + mIng
                )*/
                if(coordinateInfo.coordX == 0  && mModule == MODULE_PEN_OFFLINE) {
                    if(isJudgeOffLineEnd(coordinateInfo, true)) return;
                }

                if (mModule == MODULE_PEN_OUTLOGIN_DIALOG) {
                    Logger.e("退出登录弹框，点击无效")
                    return
                }
                if (AppManager.isRunInBackground) {
                    //程序运行在后台，点击无效
                    if (!mQDots.isEmpty()) {
                        mQDots.clear()
                    }
                    return
                }

                if(mDisDotOnOff == 1) {
                    if(!coordinateInfo.isOFFLine) {
                        return
                    }
                } else if(mDisDotOnOff == 2) {
                    if(!coordinateInfo.isOFFLine && coordinateInfo.state == PEN_DOWN_MESSAGE) {
                        mDisDotOnOff = 0
                    } else {
                        return
                    }
                }

               if ((mCoordinateInfoTemp != null
                            ) && (coordinateInfo.pageAddress != null
                            ) && (coordinateInfo.coordX != 0
                            ) && (coordinateInfo.coordY != 0
                            ) && (coordinateInfo.state == TsdService.PEN_COODINAT_MESSAGE.toInt()) && (mCoordinateInfoTemp?.state == TsdService.PEN_DOWN_MESSAGE.toInt())
                ) {
                    //按下点要重置坐标和地址，因为坐标可能是0，也可能是上一个点的弹起坐标，地址可能为null
                    //而且当前点必须是移动点，因为点击普通纸，也有单独的按下弹起点。
                    mCoordinateInfoTemp?.pageAddress = coordinateInfo.pageAddress
                    mCoordinateInfoTemp?.coordX = coordinateInfo.coordX
                    mCoordinateInfoTemp?.coordY = coordinateInfo.coordY
                    onCoordDraw_good(mCoordinateInfoTemp!!)
                }
                if (coordinateInfo.state == TsdService.PEN_DOWN_MESSAGE.toInt()) {
                } else if (coordinateInfo.state == TsdService.PEN_UP_MESSAGE.toInt()) {
                    if ((mCoordinateInfoTemp?.pageAddress != null
                                ) && (mCoordinateInfoTemp?.coordX != 0
                                ) && (mCoordinateInfoTemp?.coordY != 0
                                ) && (mCoordinateInfoTemp?.state == TsdService.PEN_COODINAT_MESSAGE.toInt())
                    ) {
                        //弹起的坐标要重置，因为坐标为0，并且弹起前一个点必须是移动点，因为点击普通纸，有单独的按下弹起两点
                        coordinateInfo.coordX = mCoordinateInfoTemp?.coordX!!
                        coordinateInfo.coordY = mCoordinateInfoTemp?.coordY!!
                        onCoordDraw_good(coordinateInfo)
                    }
                } else {
                    onCoordDraw_good(coordinateInfo)
                }
                mCoordinateInfoTemp = coordinateInfo
            }

            /*override fun onOpenPenStreamStatus(p0: Boolean, p1: String?) {
            }

            override fun onWarnActiveReport(p0: Int) {
            }

            override fun onVersionAndserialNumber(p0: String?, p1: String?, p2: String?) {
            }

            override fun onCurrentTime(p0: Long) {
            }*/

            override fun onWarnActiveReport(statusNum: Int) {
                Logger.w("onWarnActiveReport, threadName = ${Thread.currentThread().name}")
                when (statusNum) {
                    Constant.WARN_BATTERY -> Logger.w("handleActiveReport: 电池电量低警告")
                    Constant.WARN_MEMORY -> Logger.w("handleActiveReport: 存储空间警告")
                    Constant.WARN_BOOT_MODE -> Logger.w("handleActiveReport: WARN_BOOT_MODE")
                    Constant.WARN_APP_MODE -> Logger.w("handleActiveReport: WARN_APP_MODE")
                    Constant.WARN_VERIFY_MODE -> Logger.w("handleActiveReport: WARN_VERIFY_MODE")
                    Constant.WARN_NOT_VERIFY_MODE -> Logger.w("handleActiveReport: WARN_NOT_VERIFY_MODE")
                    else -> {
                    }
                }
            }

            override fun onNewSession(hardVersion: String, softVersion: String, syncNum: String) {
                //Version serial number
                Logger.i("hardVersion：$hardVersion, softVersion:$softVersion, syncNum:$syncNum, threadName = ${Thread.currentThread().name}")
            }

            override fun onPenTimeOrPenOffTime(statusNum: Int, penTime: Long) {
                if (statusNum == 1) {
                    //如果与当前系统时间差一分钟以上，自动同步时间
                    val timeMillis = System.currentTimeMillis()
                    if (Math.abs(penTime - timeMillis) > 1000 * 60) {
                        BlePenStreamManager.getInstance().setPenRTC(timeMillis)
                    }
                    val formatTime: String = mSimpleDateFormat.format(Date(penTime))
                    Logger.i("时间相差1分钟，formatTime = $formatTime, threadName = ${Thread.currentThread().name}")
                } else if (statusNum == 2) {
                    val formatTime: String = mSimpleDateFormat.format(Date(penTime))
                    Logger.i("关机时间，onPenTimeOrPenOffTime = $formatTime, threadName = ${Thread.currentThread().name}")
                }
            }
        }

        //连接回调
        mBleGattCallback = object : BleGattCallback() {
            override fun onStartConnect() {
                //Logger.i("onStartConnect, threadName = ${Thread.currentThread().name}")
                onDataReceiveListener?.onPenConnectStatus(TsdService.CONN_PEN_START, null)
                penConnectStatus(TsdService.CONN_PEN_START, null)

                if (mIsScaning) {
                    //BlePenStreamManager.getInstance().cancelScan();
                }
            }

            override fun onConnectFail(bleDevice: BleDevice, exception: BleException) {
                //Logger.i("onConnectFail, threadName = ${Thread.currentThread().name}")
                if (mFailReConnectCount < 2) {
                    mFailReConnectCount++
                    Observable.timer(1000, TimeUnit.MILLISECONDS)
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(object : Observer<Long> {
                            override fun onSubscribe(d: Disposable) {}
                            override fun onNext(aLong: Long) {
                                Logger.d("m1:onNext ${aLong}, threadName = ${Thread.currentThread().name}")
                                BlePenStreamManager.getInstance().connect(bleDevice, mBleGattCallback, mBlePenStreamCallback)
                                //BlePenStreamManager.getInstance().connect(bleDevice, mBleGattCallback)
                            }

                            override fun onError(e: Throwable) {}
                            override fun onComplete() {}
                        })
                } else {
                    ToastHelper.showNormalToast("智能笔连接失败！")
                    onDataReceiveListener?.onPenConnectStatus(TsdService.CONN_PEN_FAIL, bleDevice)
                    penConnectStatus(TsdService.CONN_PEN_FAIL, bleDevice)

                    mBleDevice = null
                }
            }

            /*override fun onConnectSuccess(p0: BleDevice?, p1: BluetoothGatt?, p2: Int) {
            }

            override fun onDisConnected(p0: Boolean, p1: BleDevice?, p2: BluetoothGatt?, p3: Int) {
            }*/

            override fun onConnectSuccess(bleDevice: BleDevice) {
                BlePenStreamManager.getInstance().setPenRTC(System.currentTimeMillis())
                //Logger.i("onConnectSuccess, threadName = ${Thread.currentThread().name}")
                if (mScanDialog.isShow){
                    mScanDialog.dismiss()
                }
                mDisDotOnOff = 0
                ToastHelper.showNormalToast("智能笔连接成功！")
                mBleDevice = bleDevice
                onDataReceiveListener?.onPenConnectStatus(TsdService.CONN_PEN_SUCCESS, bleDevice)
                penConnectStatus(TsdService.CONN_PEN_SUCCESS, bleDevice)
                LiveBusCenter.postPenIsConnectEvent("已连接")

                mPenListAdapter.setmLastDevMac(mPenMac)
                mScanDialog.dismiss()
            }

            override fun onDisConnected(isActiveDisConnected: Boolean, bleDevice: BleDevice) {
                //Logger.e("onDisConnected, threadName = ${Thread.currentThread().name}")
                ToastHelper.showNormalToast("智能笔已断开！")
                onDataReceiveListener?.onPenConnectStatus(TsdService.CONN_PEN_DIS, bleDevice)
                penConnectStatus(TsdService.CONN_PEN_DIS, bleDevice)

                mBleDevice = null
                mCoordinateInfoTemp = null
                LiveBusCenter.postPenIsConnectEvent("未连接")

                if(mModule == TsdService.MODULE_PEN_OFFLINE) {
                    setmModule(MODULE_DEFAULT)
                    progress_tv_bar?.setText("0%")
                    progress_pb_bar?.progress = 0
                    mProgressDialog.dismiss()
                    onDataReceiveListener?.onOffDrawEnd()
                }
            }
        }

        //扫描回调
        mScanCallback = object : BleScanCallback() {
            override fun onScanStarted(success: Boolean) {
                //Logger.i("onScanStarted, threadName = ${Thread.currentThread().name}")
                mIsScaning = true
                onDataReceiveListener?.onRefreshScan(mIsScaning)
                refreshScan()

            }

            override fun onLeScan(bleDevice: BleDevice) {
                super.onLeScan(bleDevice)
                //Logger.i("onLeScan, threadName = ${Thread.currentThread().name}")
            }

            override fun onScanning(bleDevice: BleDevice) {
                //Logger.i("onScanning, threadName = ${Thread.currentThread().name}")
                onDataReceiveListener?.onAddDevice(bleDevice)
                addDevice(bleDevice)

            }

            override fun onScanFinished(scanResultList: List<BleDevice>) {
                //Logger.i("onScanFinished, threadName = ${Thread.currentThread().name}")
                mIsScaning = false
                onDataReceiveListener?.onRefreshScan(mIsScaning)
                refreshScan()
            }
        }
    }

    private fun setmModule(module: Int) {
        setmModule(module, null)
    }

    private fun setmModule(module: Int, msg: String?) {
        if (mModule != module) {
            mModule = module
            Logger.e("当前处于的模式：$mModule")
        }
    }

    private fun switchOffLine() {
        setmModule(MODULE_PEN_OFFLINE)
        showDialogProgress()
        mIsGetPenInfo = false
        pollDots()
    }

    private fun onCoordDraw_good(coordinateInfo:CoordinateInfo) {
        /*Logger.e(
            ", state: " + coordinateInfo.state
                    + ", strokeNum: " + coordinateInfo.strokeNum
                    + ", pageAddress: " + coordinateInfo.pageAddress
                    + ", coordForce: " + coordinateInfo.coordForce
                    + ", x: " + coordinateInfo.coordX
                    + ", y: " + coordinateInfo.coordY
                    + ", isOFFLine: " + coordinateInfo.isOFFLine
                    + ", offLineDataAllSize: " + coordinateInfo.offLineDataAllSize
                    + ", offLineDateCurrentSize: " + coordinateInfo.offLineDateCurrentSize
                    + ", timeLong: " + coordinateInfo.timeLong
                    + ", threadName = " + Thread.currentThread().name
                    + ",img =" + mIng
        )*/

        mQDots.offer(coordinateInfo)
        pollDots()

    }

    private fun pollDots() {
        if (mIng != ING_NO) {
            //LogUtils.i("m3: mIng" + "模式未初始化")
            return
        }
        if (mQDots.isEmpty()) {
            return
        }
        if(mIsGetPenInfo) {
            return
        }
        val curDot_temp = mCurDot
        mCurDot = mQDots.poll()
        if (mCurDot == null || mCurDot!!.pageAddress == null) {
            pollDots()
            return
        }
        if(isJudgeOffLineEnd(mCurDot!!)) {
            return
        }
        if(mCurDot != null && curDot_temp != null) {
            if(mCurDot?.pageAddress != curDot_temp.pageAddress) {
                mIsMoreToast = false
            }
        } else {
            mIsMoreToast = false
        }

        if (mCurDot!!.state == PEN_DOWN_MESSAGE.toInt()) {
            val gridPicture = AppDatabase.getInstance().gridPictureDao().queryByPageserial(mCurDot!!.pageAddress)
            if (gridPicture == null) {
                mIng = ING_HTTP
                getOneNoteTemplateInfo()
            } else {
                val imgPath= getPicFilePath(
                    dataRepository.getUserId()!!,
                    "gridPicture",
                    "" + PageserialUtils.pageserialToFileName(gridPicture.pageserial),
                    ""
                )
                val imgFile: File = File(imgPath)
                if(imgFile.exists()) {
                    analysisCurDot()
                } else {
                    mIng = ING_HTTP

                    val screenWidth = ScreenUtils.getScreenWidth()
                    val scale = screenWidth * IMG_SCALE / gridPicture.width
                    val realWidth = gridPicture.width * scale
                    val realHeight = gridPicture.height * scale

                    downLoadImg(gridPicture.pictureUrl, gridPicture.pageserial, realWidth, realHeight)
                }
            }
        } else {
            analysisCurDot()
        }
    }

    private fun analysisCurDot() {
        if (mCurDot == null || mCurDot!!.pageAddress == null) {
            pollDots()
            return
        }

        if(mModule == MODULE_PEN_OFFLINE) {

            if(mCurDot?.offLineDateCurrentSize != 0 && mCurDot?.offLineDataAllSize != 0){
                val progress =
                    mCurDot!!.offLineDateCurrentSize * 100 / mCurDot!!.offLineDataAllSize
                progress_tv_bar?.setText("${progress}%")
                //Logger.e("离线进度：${progress}, ${mCurDot!!.offLineDateCurrentSize} : ${mCurDot!!.offLineDataAllSize}")
                progress_pb_bar?.progress = progress

            }

            onDataReceiveListener?.onCoordDraw(mCurDot)
            mOffLineDot_temp = mCurDot
        } else if(mModule == MODULE_PEN_TEACHING) {
            onDataReceiveListener?.onCoordDraw(mCurDot)
        } else if(mModule == MODULE_PEN_PREVIEW) {
            onDataReceiveListener?.onCoordDraw(mCurDot)
        } else {
//            Logger.e("模式出错了，点码会抛出去绘制")
            //判断是否入库，入库则跳转
            val gridPicture = AppDatabase.getInstance().gridPictureDao().queryByPageserial(mCurDot!!.pageAddress)
            gridPicture?.let {
                mIng = ING_ACTIVITY_SWITCH
                startContainerActivity(AppConstants.Router.Main.F_PREVIEW, Bundle().apply {
                    putString("pageAddress", mCurDot?.pageAddress)
                })
            }

            //onDataReceiveListener?.onCoordDraw(mCurDot)
        }

        if(mIng == ING_NO) {
            pollDots()
        }
    }

    private fun isJudgeOffLineEnd(dot: CoordinateInfo, isError : Boolean = false) : Boolean{
        if(mModule == MODULE_PEN_OFFLINE && dot?.offLineDateCurrentSize == dot?.offLineDataAllSize) {
            //离线数据的最后一个弹起点
            if(mOffLineDot_temp?.pageAddress == mCurDot?.pageAddress && !isError) {
                onDataReceiveListener?.onCoordDraw(mCurDot)
            }
            setmModule(MODULE_DEFAULT)
            progress_tv_bar?.setText("100%")
            progress_pb_bar?.progress = 100
            mProgressDialog.dismiss()
            onDataReceiveListener?.onOffDrawEnd()
            return true
        }
        return false
    }

    /**
     * 跳转容器页面
     * @param routePath Fragment路由地址
     * @param bundle    跳转所携带的信息
     */
    fun startContainerActivity(
        routePath: String?,
        bundle: Bundle? = null
    ) {
        val intent = Intent(this, ContainerFmActivity::class.java)
        intent.putExtra(ContainerFmActivity.FRAGMENT, routePath)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        if (bundle != null) {
            intent.putExtra(ContainerFmActivity.BUNDLE, bundle)
        }
        startActivity(intent)
    }


    fun refreshScan() {
        if (mIsScaning && scanAnim != null) {
            iv_scan?.startAnimation(scanAnim)
            mPenListAdapter.clearDevice()
        } else {
            iv_scan?.clearAnimation()
        }
    }

    private fun initDialogRecyclerView() {

        (mPenListAdapter as PenListAdapter).setOnItemClickListener(object :
            PenListAdapter.OnItemClickListener {
            override fun onItemClick(position: Int) {
                //Logger.i("onItemClick--$position")
                val bleDevice: BleDevice = mPenListAdapter.getBleDevice(position)
                val userId  = dataRepository.getUserId()
                val userName  = dataRepository.getUserName()
                if (mPenMac == bleDevice.mac) {
                    penConnect(bleDevice)
                } else {
                    checkTeacherMac(userId, userName, bleDevice)
                }
            }
        })
    }

    fun addDevice(bleDevice: BleDevice) {

        mPenListAdapter.addDevice(bleDevice)
        if (mPenMac == bleDevice.mac) {
            //mListener.on_ct_stopScan(); //取消扫码
            //这个账号上次连接的就是这个笔

            //自动连接
            //penConnect(bleDevice)
        }

    }

    private fun initDialogScan() {
        mScanDialog = CustomDialog.build()
            .setEnterAnimDuration(0)
            .setExitAnimDuration(0)
            .setCancelable(true)
            .setMaskColor(Color.parseColor("#4D000000"))
            .setCustomView(object : OnBindView<CustomDialog?>(R.layout.base_dialog_scan) {
                override fun onBind(dialog: CustomDialog?, v: View) {
                    iv_scan = v.findViewById<ImageView>(R.id.iv_scan)
                    iv_scan?.setOnClickListener {
                        if(mIsScaning)mBinder.on_stopScan() else mBinder.on_startScan()
                    }
                    val rv_scan : RecyclerView = v.findViewById<RecyclerView>(R.id.rv_scan)
                    rv_scan.setLayoutManager((WrapContentLinearLayoutManager(this@TsdService, LinearLayoutManager.VERTICAL, false)))
                    rv_scan.setItemAnimator(DefaultItemAnimator())
                    rv_scan.setAdapter(mPenListAdapter)
                    mPenMac = dataRepository.getPenMac()?:""
                    mPenListAdapter.setmLastDevMac(mPenMac)
                }
            })
        mScanDialog.setDialogLifecycleCallback(object : DialogLifecycleCallback<CustomDialog>() {
            override fun onShow(dialog: CustomDialog?) {
                super.onShow(dialog)
            }

            override fun onDismiss(dialog: CustomDialog?) {
                super.onDismiss(dialog)
                if(mIsScaning)mBinder.on_stopScan()
            }
        })
    }

    private fun penConnect(bleDevice: BleDevice) {
        if (!BlePenStreamManager.getInstance().isConnected(bleDevice)) {
            mScanDialog.dismiss()
            WaitDialog.show("正在连接中...")
            mBinder.on_penConnect(bleDevice)

        }
    }

    private fun showDialogScan() {
        mScanDialog.show()
        if(!mIsScaning/* && mPenListAdapter.isNullOrEmpty*/) {
            mBinder.on_startScan()
        }
    }

    private fun initDialogProgress() {
        mProgressDialog = CustomDialog.build()
            .setEnterAnimDuration(0)
            .setExitAnimDuration(0)
            .setCancelable(false)
            .setMaskColor(Color.parseColor("#4D000000"))
            .setCustomView(object : OnBindView<CustomDialog?>(R.layout.base_dialog_progress) {
                override fun onBind(dialog: CustomDialog?, v: View) {
                    progress_tv_bar = v.findViewById<TextView>(R.id.progress_tv_bar)
                    progress_pb_bar = v.findViewById<ProgressBar>(R.id.progress_pb_bar)
                }
            })
        mProgressDialog.setDialogLifecycleCallback(object : DialogLifecycleCallback<CustomDialog>() {
            override fun onShow(dialog: CustomDialog?) {
                super.onShow(dialog)
            }

            override fun onDismiss(dialog: CustomDialog?) {
                super.onDismiss(dialog)
            }
        })
    }

    private fun showDialogProgress() {
        mProgressDialog.show()

    }

    private fun checkTeacherMac(userId:Int?, teacherName:String?, bleDevice: BleDevice) {
        val json = ComUtils.getHashmapByStr(
            arrayOf("penMac", "teacherId", "teacherName"),
            arrayOf(bleDevice.mac, userId.toString(), teacherName.toString())
        )
        val requestBody = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
            GsonUtils.toJson(json))

        dataRepository.checkTeacherMac(requestBody)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : ApiSubscriptHelper<BaseBean<CheckTeacherMacBean>>() {
                override fun onResult(t: BaseBean<CheckTeacherMacBean>) {
                    if (t.code == 0) {
                        t.data?.let {
                            if(it.bindByOther) {
                                if(it.bindByOther) {
                                    //这只笔已经绑定了其他账号
                                    //弹框 谁连接了
                                    mScanDialog.dismiss()
                                    dialogCommon(3, "当前智能笔已绑定${t.data?.lastPenTeacherName}老师，\n请连接未绑定的智能笔！\n")
                                } else {
                                    //这个账号从来没有连过笔
                                    //直接连接
                                    penConnect(bleDevice)
                                }

                            }  else {
                                //该笔被其他笔绑定了
                                //这个账号连接过笔，更换笔
                                //弹框 更换笔
                                replacePenDialog(bleDevice)
                            }
                        }
                    } else {

                    }
                }


                override fun onFailed(msg: String?) {
                    ToastHelper.showNormalToast(msg)
                }
            })
    }

    private fun getOneNoteTemplateInfo() {
        val json = ComUtils.getHashmapByStr(
            arrayOf("pageserial"),
            arrayOf(mCurDot!!.pageAddress)
        )
        val requestBody = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
            GsonUtils.toJson(json))

        dataRepository.getOneNoteTemplateInfo(requestBody)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : ApiSubscriptHelper<BaseBean<GridPictureBean>>() {
                override fun onResult(t: BaseBean<GridPictureBean>) {
                    if (t.code == 0) {
                        if(t.data == null || t.data!!.pageserial.isNullOrEmpty() || t.data!!.pictureUrl.isNullOrEmpty()) {

                            Logger.e("这一页有点码，但是没有入后台库，所以这一页不入本地库")
                            ToastHelper.showNormalToast("暂未关联内容~")
                            whilePollUntileUp()
                            mIng = ING_NO
                            pollDots()
                            return
                        } else {
                            val screenWidth = ScreenUtils.getScreenWidth()
                            val scale = screenWidth * IMG_SCALE / t.data!!.width
                            val realWidth = t.data!!.width * scale
                            val realHeight = t.data!!.height * scale
                            Logger.i("屏幕宽：$screenWidth, 图片的高：${t.data!!.width}, scale: $scale，转换宽：${realWidth}, 转换高: $realHeight")


                            gridInfoToDb(t.data!!, scale)

                            downLoadImg(t.data!!.pictureUrl, t.data!!.pageserial, realWidth, realHeight)
                        }


                    } else {
                        Logger.e("请求后台获取页面数据失败1 code = ${t.code}")
                        if (t.code == 200005 || t.code == 200006|| t.code == 200007){
                            if(!mIsMoreToast) {
                                mIsMoreToast = true
                                ToastHelper.showNormalToast(t.msg)
                                delaySetToastFalse()
                            }
                        }
                        whilePollUntileUp()
                        mIng = ING_NO
                        pollDots()
                    }
                }
                override fun onFailed(msg: String?) {
                    ToastHelper.showNormalToast(msg)
                    Logger.e("请求后台获取页面数据失败2")
                    whilePollUntileUp()
                    mIng = ING_NO
                    pollDots()
                }
            })
    }

    private fun delaySetToastFalse() {
        Observable.timer(10000, TimeUnit.MILLISECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<Long> {
                override fun onSubscribe(d: Disposable) {}
                override fun onNext(aLong: Long) {
                    mIsMoreToast = false;
                }

                override fun onError(e: Throwable) {}
                override fun onComplete() {}
            })
    }

    private fun downLoadImg(url:String, pageserial: String, realWidth: Float, realHeight: Float) {
        val imgPath= getPicFilePath(
            dataRepository.getUserId()!!,
            "gridPicture",
            "" + PageserialUtils.pageserialToFileName(pageserial),
            ""
        )
        val imgFile: File = File(imgPath)
        if(imgFile.exists()) {
            Logger.e("图片已经存在，不需要下载")
            mIng = ING_NO
            analysisCurDot()
            return
        }
        Glide.with(this).asBitmap().load(url).skipMemoryCache(true)
            .diskCacheStrategy(DiskCacheStrategy.NONE)
            .override(realWidth.toInt(), realHeight.toInt())
            .into(object : CustomTarget<Bitmap?>() {
                override fun onResourceReady(
                    resource: Bitmap,
                    transition: Transition<in Bitmap?>?
                ) {
                    Logger.e("m1:Glde图片-成功  = " + url)
                    Observable.create<Int> { emitter ->
                        saveBitmap(resource, imgPath!!)
                        emitter.onNext(1)
                        emitter.onComplete()
                    }.subscribeOn(Schedulers.newThread())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(object : Observer<Int?> {
                            override fun onSubscribe(d: Disposable) {
                            }

                            override fun onNext(t: Int) {
                                mIng = ING_NO
                                analysisCurDot()
                            }

                            override fun onError(e: Throwable) {
                            }

                            override fun onComplete() {
                            }

                        })
                }

                override fun onLoadFailed(errorDrawable: Drawable?) {
                    super.onLoadFailed(errorDrawable)
                    Logger.e("m1:Glde图片-失败  = " + url)
                    //图片已经损坏要处理
                    if(imgFile.exists()) {
                        imgFile.delete()
                    }

                    whilePollUntileUp()
                    mIng = ING_NO
                    pollDots()

                }

                override fun onLoadCleared(placeholder: Drawable?) {}
            })
    }

    private fun whilePollUntileUp() {
        while (!mQDots.isEmpty()) {
            val info = mQDots.poll()
            isJudgeOffLineEnd(info!!)
            if (info.state == PEN_UP_MESSAGE.toInt()) {
                break
            }
            //循环弹出数据，直到弹出下一个弹起数据
        }
    }

    private fun gridInfoToDb(grid: GridPictureBean, scale:Float) {
        AppDatabase.getInstance().gridPictureDao().insertAll(GridPicture(grid.id.toLong(),
            grid.pageserial, grid.pageNumber, grid.pictureName, grid.pictureUrl, grid.width, grid.height,
            scale, grid.bookId.toLong()))
    }

    private fun teacherConfirmConnect(userId:Int?, userName:String?, bleDevice: BleDevice) {
        val json = ComUtils.getHashmapByStr(
            arrayOf("penMac", "teacherId", "teacherName"),
            arrayOf(bleDevice.mac, userId.toString(), userName!!)
        )
        val requestBody = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
            GsonUtils.toJson(json))

        dataRepository.teacherConfirmConnect(requestBody)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : ApiSubscriptHelper<BaseBean<Any?>>() {
                override fun onResult(t: BaseBean<Any?>) {
                    if (t.code == 0) {
                        dataRepository.savePenMac(bleDevice.mac)
                    } else {

                    }
                }


                override fun onFailed(msg: String?) {
                    ToastHelper.showNormalToast(msg)
                }
            })
    }
    fun penConnectStatus(code: Int, bleDevice: BleDevice?) {
        if (code == CONN_PEN_START) {
            //DialogXUtil.waitDialogShow("正在连接中...");
        } else if (code == CONN_PEN_FAIL) {
            WaitDialog.dismiss()
            //Toast.makeText(getActivity(), "蓝牙连接失败", Toast.LENGTH_LONG).show();
        } else if (code == CONN_PEN_SUCCESS) {
            WaitDialog.dismiss()
            val userId  = dataRepository.getUserId()
            val userName  = dataRepository.getUserName()
            if (bleDevice?.mac != mPenMac) {
                //只有不更新新的笔时，才更新后台
                teacherConfirmConnect(userId, userName, bleDevice!!)
            }
            mPenMac = bleDevice.mac
            mIsGetPenInfo = true
            //BlePenStreamManager.getInstance().getPenInfo()
            setRealtimeMode() //这一句已经包含上面那句获得比信息的接口
        } else if (code == CONN_PEN_DIS) {
            WaitDialog.dismiss()
        }
    }

    private fun setRealtimeMode() {
        Observable.timer(50, TimeUnit.MILLISECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<Long> {
                override fun onSubscribe(d: Disposable) {}
                override fun onNext(aLong: Long) {
                    //Logger.i("-----------setRealtimeMode")
                    BlePenStreamManager.getInstance().setRealtimeMode()
                }

                override fun onError(e: Throwable) {}
                override fun onComplete() {}
            })
    }


    /**
     * type: 1:蓝牙弹框， 2:GPS弹框， 3:笔已连接弹框
     * */
    private fun dialogCommon(type:Int, msg:String) {
        val customDialog: CustomDialog = CustomDialog.build()
            .setEnterAnimDuration(0)
            .setExitAnimDuration(0)
            .setMaskColor(Color.parseColor("#4D000000"))
            .setCustomView(object : OnBindView<CustomDialog?>(R.layout.base_dialog_common) {
                override fun onBind(dialog: CustomDialog?, v: View) {
                    val btn_ok = v.findViewById<Button>(R.id.btn_ok) as Button
                    btn_ok.setOnClickListener {
                        dialog?.dismiss()
                        if(type == 1) {
                            val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK )
                            startActivity(intent)
                        } else if(type ==2) {
                            val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK )
                            startActivity(intent)
                        } else if(type == 3) {
                            showDialogScan()
                        }
                    }
                    val tv_msg = v.findViewById<TextView>(R.id.tv_msg) as TextView
                    tv_msg.setText(msg)
                }
            })
        customDialog.show()
    }

    private fun replacePenDialog(posBle:BleDevice) {
        val customDialog: CustomDialog = CustomDialog.build()
            .setEnterAnimDuration(0)
            .setExitAnimDuration(0)
            .setMaskColor(Color.parseColor("#4D000000"))
            .setCustomView(object : OnBindView<CustomDialog?>(R.layout.base_dialog_replace) {
                override fun onBind(dialog: CustomDialog?, v: View) {
                    val tv_ok = v.findViewById<TextView>(R.id.tv_ok) as TextView
                    val tv_cancel = v.findViewById<TextView>(R.id.tv_cancel) as TextView
                    tv_ok.setOnClickListener {
                        dialog?.dismiss()
                        penConnect(posBle)
                    }
                    tv_cancel.setOnClickListener {
                        dialog?.dismiss()
                        showDialogScan()
                    }
                }
            })
        customDialog.show()
    }

    /// ===========================================================
    private var onDataReceiveListener: OnDataReceiveListener? = null

    interface OnDataReceiveListener {
        fun onErrTsdPen(code: Int, msg: String?)
        fun onRefreshScan(isScaning: Boolean)
        fun onAddDevice(bleDevice: BleDevice?)
        fun onPenConnectStatus(code: Int, bleDevice: BleDevice?)
        fun onGetPenInfo(batteryPercent: Int, memoryPercent: Int, memoryByteNum: Int)
        fun onCoordDraw(coordinateInfo: CoordinateInfo?)
        fun onOffDrawEnd()
        fun onFinish() //防止退出界面时，当前界面监听为null,导致很多问题
    }

    fun setOnDataReceiveListener(dataReceiveListener: OnDataReceiveListener?) {
        onDataReceiveListener = dataReceiveListener
    }

    fun initViewObservable() {
        LiveBusCenter.observeLogoutEvent(this){
            if(it.code == 1) {
                //弹出退出登录弹框
               setmModule(MODULE_PEN_OUTLOGIN_DIALOG)
            } else if(it.code == 2) {
                //取消退出登录弹框
                setmModule(MODULE_DEFAULT)
            } else if(it.code == 3) {
                setmModule(MODULE_DEFAULT)
                //退出登录成功
                if (mBinder.on_getPenIsConnect()) {
                    BlePenStreamManager.getInstance().disconnect(mBleDevice)
                }
            } else if(it.code == 4){ //强制退出登录
                if (dataRepository.isLogin()){
                    AppManager.isRunInBackground
                    LiveBusCenter.postLogoutEvent(3)
                    startContainerActivity(AppConstants.Router.Login.F_LOGIN)
                    AppManager.instance.finishAllActivity()
                    dataRepository.saveLoginState(false)
                }
            }
        }
        LiveBusCenter.observeLoginSuccessEvent(this) {
            dataRepository.getTeacherPen()
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : ApiSubscriptHelper<BaseBean<TeacherPenBean>>() {
                    override fun onResult(t: BaseBean<TeacherPenBean>) {
                        if (t.code == 0) {
                            t.data?.let {
                                if(it.isBind == 1) {
                                    mPenMac = t.data?.penMac ?: ""
                                    dataRepository.savePenMac(mPenMac)
                                    mPenListAdapter.setmLastDevMac(mPenMac)
                                } else {
                                    mPenMac = ""
                                    dataRepository.savePenMac(mPenMac)
                                }
                            }

                        } else {

                        }
                    }


                    override fun onFailed(msg: String?) {
                        ToastHelper.showNormalToast(msg)
                    }
                })
        }

        LiveBusCenter.observeToPenNoSubmitEvent(this){
            toGrid_noSubmit()
        }

        LiveBusCenter.observeToVersionUpdateEvent(this){
            //mAppVersionDialog.show()
            queryLastVersion(it.isSet)
        }
    }

    private fun toGrid_noSubmit() {
        mGridPictureList_noSubmit =  AppDatabase.getInstance().gridNoSubmitDao().getAll()

        if(mGridPictureList_noSubmit.isNotEmpty() && mUploadCount_noSubmit == -1) {
            uploadImg_noSubmit()
        }
    }

    private fun uploadImg_noSubmit() {
        mCommitOfflineReqS_noSubmit.clear()
        mUploadCount_noSubmit = 0
        //WaitDialog.show("正在上传数据")
        uploadImg_http_noSubmit()
    }

    private fun uploadImg_http_noSubmit() {
        if(mUploadCount_noSubmit != mGridPictureList_noSubmit.size) {
            val imgPath= getPicFilePath(
                dataRepository.getUserId()!!,
                "gridPicture",
                "" + PageserialUtils.pageserialToFileName(mGridPictureList_noSubmit.get(mUploadCount_noSubmit).pageserial),
                ""
            )
            if(!File(imgPath).exists()) {
                return
            }
            val uploadPath = "${dataRepository.getUserId()!!}_${PageserialUtils.pageserialToFileName(mGridPictureList_noSubmit.get(mUploadCount_noSubmit).pageserial)}_${System.currentTimeMillis()}.png"
            UploadOss.getInstance()?.upload2OSS(uploadPath, imgPath!!, onUploadListener_noSubmit, null)
        } else {
            commitTeacherBookNote_noSubmit()
        }
    }


    private fun commitTeacherBookNote_noSubmit() {
        val requestBody = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
            GsonUtils.toJson(mCommitOfflineReqS_noSubmit))

        dataRepository.commitTeacherBookNote(requestBody)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : ApiSubscriptHelper<BaseBean<Any?>>() {
                override fun onResult(t: BaseBean<Any?>) {
                    if(t.code == 0) {
                        AppDatabase.getInstance().gridNoSubmitDao().deleteAll()
                        mGridPictureList_noSubmit = listOf<GridNoSubmit>()
                        mCommitOfflineReqS_noSubmit.clear()
                        mUploadCount_noSubmit = -1
                    }
                }

                override fun onFailed(msg: String?) {
                    ToastHelper.showNormalToast(msg)
                }
            })

    }

    val onUploadListener_noSubmit = object : UploadOss.OnUploadListener{
        override fun uploadSuccess(uploadFile: String?, localFile: String, ossParams: OssParams?): String? {
            if(mUploadCount_noSubmit < mGridPictureList_noSubmit.size && mUploadCount_noSubmit >= 0) {
                mCommitOfflineReqS_noSubmit.add(
                    CommitOfflineReq(
                        uploadFile!!,
                        mGridPictureList_noSubmit.get(mUploadCount_noSubmit).pageserial
                    )
                )
                mUploadCount_noSubmit++
                uploadImg_http_noSubmit()
            }
            return null
        }

        override fun uploadFail(
            uploadFile: String?,
            localFile: String,
            ossParams: OssParams?,
        ): String? {
            mUploadCount_noSubmit = -1
            return null
        }
    }

    private fun initDialogAppVersion() {
        mAppVersionDialog = CustomDialog.build()
            .setEnterAnimDuration(0)
            .setExitAnimDuration(0)
            .setCancelable(false)
            .setMaskColor(Color.parseColor("#4D000000"))
            .setCustomView(object : OnBindView<CustomDialog?>(R.layout.base_dialog_appversion) {
                override fun onBind(dialog: CustomDialog?, v: View) {
                    /*iv_scan = v.findViewById<ImageView>(R.id.iv_scan)
                    iv_scan?.setOnClickListener {
                        if(mIsScaning)mBinder.on_stopScan() else mBinder.on_startScan()
                    }*/
                    val tv_version_desc = v.findViewById<AppCompatTextView>(R.id.tv_version_desc)
                    tv_version_desc.text = mStrVersionDesc
                    val bt_ok = v.findViewById<AppCompatButton>(R.id.bt_ok)
                    bt_ok?.setOnClickListener {
                        //dialog!!.dismiss()
                        startContainerActivity(AppConstants.Router.User.F_UPDATE, Bundle().apply {
                            putString("version", mVersionInfo.version)
                            putString("downloadUrl", mVersionInfo.dowloadUrl)
                            putInt("apkSize", mVersionInfo.apkSize)
                        })
                    }
                }
            })


        mAppVersionDialog.setDialogLifecycleCallback(object : DialogLifecycleCallback<CustomDialog>() {
            override fun onShow(dialog: CustomDialog?) {
                super.onShow(dialog)
            }

            override fun onDismiss(dialog: CustomDialog?) {
                super.onDismiss(dialog)
            }
        })
    }

    private fun queryLastVersion(isSet: Boolean) {
        var apkDir = getDowApkDir(
            dataRepository.getUserId()!!,
            "apk"
        )
        FileUtils.deleteAllInDir(apkDir)

        apkDir = getDowApkDir(
            0,
            "apk"
        )
        FileUtils.deleteAllInDir(apkDir)

        dataRepository.queryLastVersion(AppUtils.getAppVersionName() /*"0.0.9"*/)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : ApiSubscriptHelper<BaseBean<VersionInfoBean>>() {
                override fun onResult(t: BaseBean<VersionInfoBean>) {
                    if(t.code == 0) {
                        if(t.data == null) {
                            if(isSet) {
                                ToastHelper.showNormalToast("已经是最新版本")
                            }
                        } else if(t.data!!.status == 0 || t.data!!.isDeleted == 1) {
                            Logger.e("版本已经禁用和删除")
                        } else {
                            val description = if(t.data!!.description == null)"" else t.data!!.description
                            mStrVersionDesc = "版本: ${t.data!!.version}\n大小: ${t.data!!.apkSize/1024/1024}M\n更新内容: ${description}"
                            mVersionInfo = t.data!!;
                            if(!mAppVersionDialog.isShow) {
                                mAppVersionDialog.show()
                            }
                        }
                    } else {
                        ToastHelper.showNormalToast(t.msg)
                    }
                }

                override fun onFailed(msg: String?) {
                    ToastHelper.showNormalToast(msg)
                }
            })

    }

}